﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
using Microsoft.Phone.Controls;
using System.Collections.ObjectModel;
using FbHelperWP7NonUi.TextHelpers;

namespace FbHelperWP7.Pages
{
    public partial class TextReadPage : PhoneApplicationPage
    {
        private sealed class TextPosition
        {
            public static readonly TextPosition StartPosition = new TextPosition( 0, 0 );

            public TextPosition( int endPosition, int wordCount )
            {
                EndPosition = endPosition;
                WordCount = wordCount;
            }

            public readonly int EndPosition;
            public readonly int WordCount;

            public override string ToString()
            {
                return String.Format( "EndPosition: {0}; WordCount: {1}", EndPosition, WordCount );
            }
        }

        private const int maxTestBlockHeightMultiplier = 256;

        private readonly Stack<TextPosition> navidationHistory = new Stack<TextPosition>();

        private readonly List<AbstractTextPart> text = new List<AbstractTextPart>();

        private TextPosition nextPosition = null;

        public TextReadPage()
        {
            InitializeComponent();

            if ( NavigationCache.Book != null )
            {
                text.Clear();

                text.AddRange( NavigationCache.Book.Text );
            }

            navidationHistory.Push( TextPosition.StartPosition );
        }

        /// <summary>
        /// Обновляет данные на экране в соответствии с данными в стеке.
        /// </summary>
        /// <returns>Позиция, с которой начинается следующая страница</returns>
        private TextPosition UpdateVisual()
        {
            if ( textBlockLayout.ActualHeight <= 0 )
                throw new Exception( "Wrong contron state" );

            TextPosition fromPosition = navidationHistory.Peek();

            mainTextBlock.Inlines.Clear();

            UpdateTestTextBlockProperties();

            for ( int newEndPosition = fromPosition.EndPosition; newEndPosition < text.Count; newEndPosition++ )
            {
                int startFromWord = newEndPosition == fromPosition.EndPosition ? fromPosition.WordCount : 0;
                AbstractTextPart currentTextEntry = text[ newEndPosition ];
                int entryWordCount = currentTextEntry.GetWordCount();

                //ToDo: add optimization: try to add entry paragraph

                int newWordCount;
                for ( newWordCount = startFromWord; newWordCount < entryWordCount; newWordCount++ )
                {
                    mainTextBlock.Inlines.Add( CreateRun( currentTextEntry.CreateTextVisual( startFromWord, newWordCount ) ) );

                    bool isValid = IsTestTextBlockValid();

                    mainTextBlock.Inlines.RemoveAt( mainTextBlock.Inlines.Count - 1 );

                    if ( !isValid )
                        break;
                }

                if ( newWordCount >= entryWordCount )//Inner loop ended successfully
                {
                    mainTextBlock.Inlines.Add( CreateRun( currentTextEntry.CreateTextVisual( startFromWord ) ) );
                    continue;
                }

                newWordCount--;

                if ( newWordCount > 0 )//Если хоть что-то от этого блока влезло
                {
                    mainTextBlock.Inlines.Add( CreateRun( currentTextEntry.CreateTextVisual( startFromWord, newWordCount ) ) );

                    return new TextPosition( newEndPosition, newWordCount );
                }

                //В противном случае вставляем выдаем конец предыдущего параграфа
                return new TextPosition( newEndPosition - 1, text[ newEndPosition ].GetWordCount() );
            }

            throw new Exception( "Word is too long" );
        }

        private bool IsTestTextBlockValid()
        {
            mainTextBlock.Measure( new Size( mainTextBlock.MaxWidth, mainTextBlock.MaxHeight ) );
            mainTextBlock.InvalidateMeasure();

            if ( mainTextBlock.ActualWidth > textBlockLayout.ActualWidth )
                return false;

            return
                !double.IsNaN( mainTextBlock.ActualHeight ) &&
                !double.IsNaN( textBlockLayout.ActualHeight ) &&
                textBlockLayout.ActualHeight > 0 &&
                mainTextBlock.ActualHeight < textBlockLayout.ActualHeight;
        }

        private static Run CreateRun( StyledText styledText )
        {
            return new Run()
            {
                Text = styledText.Text,
                FontSize = styledText.Style.FontSize,
                FontStyle = styledText.Style.IsItalic ? FontStyles.Italic : FontStyles.Normal,
                FontWeight = styledText.Style.IsBold ? FontWeights.Bold : FontWeights.Normal
            };
        }

        private void UpdateTestTextBlockProperties()
        {
            mainTextBlock.MaxWidth = textBlockLayout.ActualWidth;
            mainTextBlock.MaxHeight = textBlockLayout.ActualHeight * maxTestBlockHeightMultiplier;
        }

        private void mainTextBlock_SizeChanged( object sender, SizeChangedEventArgs e )
        {
            nextPosition = UpdateVisual();
        }

        private void textBlockLayout_Tap( object sender, GestureEventArgs e )
        {
            Point where = e.GetPosition( textBlockLayout );

            double height = textBlockLayout.ActualHeight;

            if ( where.Y > ( height * 2 ) / 3 )
                GoToNextPage();

            if ( where.Y < height / 3 )
                GoToPreviousPage();
        }

        private void GoToPreviousPage()
        {
            if ( navidationHistory.Count > 1 )//Назад 
                navidationHistory.Pop();

            nextPosition = UpdateVisual();
        }

        private void GoToNextPage()
        {
            navidationHistory.Push( nextPosition );

            nextPosition = UpdateVisual();
        }
    }
}